package pl.polidea.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import pl.polidea.nativebuffer.NativeBuffer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class NativeImageView extends ImageView {
    static List<Bitmap> bitmaps = Collections.synchronizedList(new ArrayList<Bitmap>());

    // private Bitmap[] bitmap;
    int[] pointer;
    private int bWidth;
    private int bHeight;
    private Config config;
    private int mipLevel = -1;
    private int mipLevels;
    private boolean loaded = false;

    public NativeImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setImageBitmap(String bm) {
        Bitmap b;
        while (true) {
            try {
                b = BitmapFactory.decodeFile(bm);
                break;
            } catch (OutOfMemoryError e) {
                // if (bitmaps.size() > 0) {
                // bitmaps.get(0).recycle();
                // // bitmaps.remove(0);
                // }
            }
        }
        if (b == null) {
            return;
        }

        this.bWidth = b.getWidth();
        this.bHeight = b.getHeight();
        this.config = b.getConfig();

        mipLevels = (int) Math.floor(Math.min(Math.log(bWidth) / Math.log(2.0), Math.log(bHeight) / Math.log(2.0))) + 1;
        mipLevels = Math.min(mipLevels, 5);
        mipLevel = mipLevels - 1;
        pointer = new int[mipLevels];
        pointer[0] = NativeBuffer.setBitmapData(b);

        // bitmap = new Bitmap[mipLevels];

        for (int i = 1; i < mipLevels; i++) {
            while (true) {
                try {
                    Bitmap scaled = Bitmap.createScaledBitmap(b, (int) (bWidth / Math.pow(2.0f, i)),
                            (int) (bHeight / Math.pow(2.0f, i)), true);
                    pointer[i] = NativeBuffer.setBitmapData(scaled);
                    scaled.recycle();
                    break;
                } catch (OutOfMemoryError e) {
                } catch (NullPointerException e2) {
                    return;
                }
            }
        }

        b.recycle();

        post(new Runnable() {
            public void run() {
                // NativeImageView.this.setImageBitmap(b2);
                synchronized (bitmaps) {
                    NativeImageView.this.loaded = true;
                }
            }
        });
    }

    // static List<Bitmap> bitmaps = Collections.synchronizedList(new
    // ArrayList<Bitmap>());

    public void setMipLevel(int level) {
        if (mipLevel != level) {
            synchronized (bitmaps) {
                if (getDrawable() != null && ((BitmapDrawable) getDrawable()).getBitmap() != null) {
                    if (!((BitmapDrawable) getDrawable()).getBitmap().isRecycled())
                        ((BitmapDrawable) getDrawable()).getBitmap().recycle();
                    bitmaps.remove(((BitmapDrawable) getDrawable()).getBitmap());
                    super.setImageBitmap(null);
                }
                this.mipLevel = Math.max(0, Math.min(level, mipLevels - 1));
            }
        }
    }

    @Override
    public void draw(Canvas canvas) {
        boolean loaded;
        synchronized (this) {
            loaded = this.loaded;
        }
        if (!loaded)
            return;
        synchronized (bitmaps) {
            while (true) {
                try {
                    if (getDrawable() == null || ((BitmapDrawable) getDrawable()).getBitmap() == null
                            || ((BitmapDrawable) getDrawable()).getBitmap().isRecycled()) {
                        Bitmap b = Bitmap.createBitmap((int) (bWidth / Math.pow(2.0f, mipLevel)),
                                (int) (bHeight / Math.pow(2.0f, mipLevel)), config);
                        NativeBuffer.getBitmapData(pointer[mipLevel], b);
                        setImageBitmap(b);
                        bitmaps.add(b);
                    }
                    super.draw(canvas);
                    break;
                    // b.recycle();
                } catch (OutOfMemoryError e) {
                    if (bitmaps.size() == 0)
                        break;
                    Bitmap b = bitmaps.get(0);
                    bitmaps.remove(0);
                    b.recycle();
                }
            }
        }
    }

    public int getMipLevels() {
        return mipLevels;
    }
}
